package Redis

import (
	"fmt"
	"queue/facade/frameLog"
	"queue/library/common"
	"runtime"
	"sync"
	"time"
)

type RedisPool struct {
	//将 redisClient中的连接池相关业务转移到这里
	host           string
	password       string
	netType        string
	Length         int //连接池总长度，Count < Length 的时候允许继续创建连接
	clientPool     chan *RedisConf
	Count          int           //连接池中已经创建的连接数
	CheckFrequency time.Duration //检查连接池使用状态的频率 (0)
}

var Pool *RedisPool
var DefaultCreateNum = 100 /* *runtime.NumCPU()*/ //默认创建连接数
//var DefaultCreateNum = 90 //压测调试，直接创到最大
var EveryAddNum = runtime.NumCPU() //默认连接不够且连接池没满时每次追加连接数
var lockAdd = sync.Mutex{}         //避免动态添加线程池时多个请求同时执行而导致程序阻塞

//在初始项目时调创建指定长度的redis连接池
func NewRedisPool(length int, host, password, netType string) {
	if length < 1 {
		// 连接池socket连接数小于1时，默认为2倍CPU数， 2 * runtime.NumCPU()
		length = 2 * runtime.NumCPU()
	}
	Pool = &RedisPool{
		Length:         length,
		clientPool:     make(chan *RedisConf, length),
		host:           host,
		password:       password,
		netType:        netType,         //tcp 、udp
		CheckFrequency: 2 * time.Minute, //默认2分钟
	}
	// 默认创建10连接，调用GetRedisConn()时再创建
	if length >= DefaultCreateNum {
		lockAdd.Lock()
		for i := 0; i < DefaultCreateNum; i++ {
			Pool.Count += 1
			Pool.AddRedisCoon(NewRedisConnect(Pool.host, Pool.password, Pool.netType, 0))
		}
		lockAdd.Unlock()
	}
	//go 一个协程定时检测连接池中连接的使用状态
	go Pool.CheckUseTime()
}

//检查连接池中连接使用状态 todo 这个方法可以先不写
func (rp *RedisPool) CheckUseTime() {
	// 【多长时间循环执行一次？多长时间没使用的连接要清除？连接池最少保留多少个连接？？】

	for {
		// 定时将连接池中的连接全部取出，对最后使用时间做判断，如果长时间没使用的话就将连接从连接池中删除
		<-time.After(rp.CheckFrequency) //2分钟执行一次检查
		//conn:=rp.GetRedisConn() 如果长时间没用 rp.Count-=1，否则 rp.AddRedisCoon(conn)将连接放回连接池
	}
}

// 从Pool.clientPool连接池中取出一个实例
func (rp *RedisPool) GetRedisConn(logId string) *RedisConf {
	if rp.Count == 0 { //连接池中没有连接时创建一个，否则下面读取就会永久阻塞
		rp.Count += 1
		rp.AddRedisCoon(NewRedisConnect(rp.host, rp.password, rp.netType, 0))
	}
	timer := common.NewTimer()
	conn := <-rp.clientPool
	spendTime := timer.SpendMillisecond()
	// 当连接池没满时，如果获取连接超过500毫秒则往连接池添加一个连接
	if spendTime > 500 {
		if rp.Count < rp.Length {
			lockAdd.Lock() //只允许一个协程进行添加线程池
			addNum := EveryAddNum
			if rp.Length-rp.Count < addNum {
				addNum = rp.Length - rp.Count
			}
			for i := 0; i < addNum; i++ {
				rp.Count += 1
				rp.AddRedisCoon(NewRedisConnect(rp.host, rp.password, rp.netType, 0))
			}
			lockAdd.Unlock()
		} else { //此时连接池配小了，应做日志提示
			//fmt.Println("redis 接池配小了",spendTime)
		}
	}
	conn.ConnectUseCount++ //连接被使用的次数加 1
	conn.SetLogId(logId)   //设置请求 LogId
	// todo 如何检测连接是否可用？？
	return conn
}

//向连接池中添加一个连接
func (rp *RedisPool) AddRedisCoon(rc *RedisConf) {
	rp.clientPool <- rc
}

func Get(logId, keyName string) (string, error) {
	conf := Pool.GetRedisConn(logId) // 从Pool.AddRedisCoon连接池中取出一个实例
	s, e := conf.Get(keyName)        //调用实例的 Get方法得到结果 result , err
	//fmt.Println(s, e)
	if e != nil {
		frameLog.Error(e) //记录错误日志
	}
	Pool.AddRedisCoon(conf) //将用完的实例还回连接池
	// 返回结果result
	return s, e
}
func Set(logId string) {
	// 从Pool.clientPool连接池中取出一个实例
	//调用实例Set方法向rides中写入数据
	//将实例放回连接池
}

//清空并关闭连接池中所有连接
func CloseRedisPool() {
	fmt.Println("close", Pool)
	close(Pool.clientPool)
	for rc := range Pool.clientPool {
		rc.Close()
	}
	Pool.Length = 0
	Pool.clientPool = nil
}

// 更多方法调用 ······
